﻿using Core.DataAccess.ApiJson.SearchService;
using Core.DataAccess.ApiJson.SearchServiceProviders;
using Core.DataAccess.Model;
using Core.Framework;
using Core.Framework.Common;
using DotNetty.Framework;
using DotNetty.Framework.BaseTriggers;
using DotNetty.Framework.ExtendChannelOfClient;
using iTool.Connector.ModelOfRequest;
using DotNetty.OrleansClient;
using Elasticsearch.Net;
using Local.Implement;
using Local.Interface;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Orleans;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace iTool.Connector
{
    public class MainProvider
    {
        DotNettyService<QueryMessageRequest, FunctionHttpContext> dotNetty;
        ManualResetEvent manual;
        readonly string _iToolConnectorProcessPath = "/var/iTool/client.process";
        private readonly string _commandPath = "./command.sh";

        public MainProvider(ManualResetEvent manual)
        {
            this.manual = manual;
#if DEBUG
            _iToolConnectorProcessPath = $"D:/iTool-Cloud-Framework/client.process";
            //_iToolServiceProcessPath = $"{Directory.GetCurrentDirectory()}/client.process";
#elif CLOUD
            _iToolConnectorProcessPath = $"D:/iTool-Cloud-Framework/client.process";
#endif
            if (!File.Exists(_commandPath))
            {
                File.Create(_commandPath).Close();
                Executer.RunInLinux("chmod u+x " + _commandPath);
            }
        }

        public async Task StartAsync()
        {
            try
            {
                Console.WriteLine("iTool> Hello World!\n");

                // Write State
                this.WriteProcessState(ProcessStatusEnum.Staring);

                // 1. Build Service Provider
                IServiceCollection services = await this.BuildServiceCollection();

                // 2. Create Netty Object
                dotNetty = new DotNettyService<QueryMessageRequest, FunctionHttpContext>(services);

                await dotNetty.StartAsync();

                var appSettingBuilder = ProviderOfServices.GetService<AppSettingBuilder>();
                appSettingBuilder.Ini();

                // 3. 上报状态
                this.WriteProcessState(ProcessStatusEnum.Started);
            }
            catch
            {
                this.WriteProcessState(ProcessStatusEnum.StartError);
                manual.Set();
            }

        }

        public async Task ReStartAsync()
        {
            Console.WriteLine($"{DateTime.Now} ，等待停止信号！\n");
            this.WriteProcessState(ProcessStatusEnum.WaitingStopSignal);

            // 等待停止信号
            var fileChanged = new iToolFileChangeDetect(_iToolConnectorProcessPath);
            fileChanged.StartWaitResult(value => value == ProcessStatusEnum.BeginStop.ToString(), 10 * 1000);

            TimerJob.Stop();
            await this.RandomDelay();
            await this.StopAsync(true);

            // 清空服务
            ProviderOfServices.Clear();

            // GC
            GC.Collect();

            Console.WriteLine($"{DateTime.Now} ，等待启动信号！\n");
            this.WriteProcessState(ProcessStatusEnum.WaitingStartSignal);
            // 等待启动信号
            var fileChanged2 = new iToolFileChangeDetect(_iToolConnectorProcessPath);
            fileChanged2.StartWaitResult(value => value == ProcessStatusEnum.BeginStart.ToString(), 180 * 1000);
            LoggerUtils.Write($"call client start", "log_service");

            await this.RandomDelay(3333);
            await this.StartAsync();
        }

        public async Task StopAsync(bool isReStart = false)
        {
            if (dotNetty == null)
            {
                return;
            }

            try
            {
                Console.WriteLine($"{DateTime.Now} ，准备进行资源清理！");
                this.WriteProcessState(ProcessStatusEnum.Stoping);

                // 1. Shut down netty
                await dotNetty.ShutdownAsync();

                // 2. Close ICluster
                var cluster = this.GetService<IClusterClient>();
                await cluster.Close();
                Console.WriteLine($"Orleans IClusterClient Closed.");

                // 3. 上报状态

                if (!isReStart)
                {
                    dotNetty = null;

                    // 5. 通知上层退出
                    Console.WriteLine($"{DateTime.Now} ，服务程序已正常退出！");
                }
            }
            finally //(Exception ex)
            {
                this.WriteProcessState(ProcessStatusEnum.Stoped);
                this.StopSystemctl();
                this.manual.Set();
            }
        }

        private async Task<IServiceCollection> BuildServiceCollection()
        {
            IServiceCollection services = ProviderOfServices.GetServiceCollection();
            IServiceCollection httpWorkerServices = ProviderOfServices.GetServiceCollection("HttpWorker");
            
            // 1. REG Data ElasticSearch Provider
            if (AppSetting.iToolConfigure.ElasticSearchProviders.Any() && false)
            {
                var first = AppSetting.iToolConfigure.ElasticSearchProviders.First();
                Uri elasticSearchUri = new Uri($"http://{first.Ip}:{first.Port}");
                ConnectionConfiguration connection = new ConnectionConfiguration(elasticSearchUri);
                ElasticLowLevelClient elasticLowLevelClient = new ElasticLowLevelClient(connection);
                services.AddSingleton(elasticLowLevelClient);
                services.AddSingleton<ISearchProvider, ElasticSearchProvider>();

                httpWorkerServices.AddSingleton(elasticLowLevelClient);
                httpWorkerServices.AddSingleton<ISearchProvider, ElasticSearchProvider>();
            }
            else
            {
                services.AddSingleton<ISearchProvider, EmptyProvider>();
                httpWorkerServices.AddSingleton<ISearchProvider, EmptyProvider>();
            }

            // 2. REG CloudFunction Edit Provider
            services.AddSingleton<ICloudFunction, CloudFunction>();

            // 3. REG NodePackage Provider
            services.AddSingleton<INodeInstallPackage, NodeInstallPackage>();

            // 4. REG SqlServer Provider
            services.AddSingleton<IDBFactory, SQLServer>();

            // 5. REG Logger Provider
            services.UseiToolLogger();
            httpWorkerServices.UseiToolLogger();

            // 6. WebSocket Handel
            services
                .AddSingleton<IWebSocketEventTriggered<QueryMessageRequest>, WebSocketEventTriggered<QueryMessageRequest>>()
                .AddSingleton<IBindChannelInfo, JWTBindChannelInfo>()
                .AddTransient<WebSocketTaskHandler>();

            // 7. Http Handel
            services
                .AddSingleton<IHttpEventTriggered<FunctionHttpContext>, HttpEventTriggered<FunctionHttpContext>>();

            // 8. REG Orleans Client
            var iToolConnectorProvider = new iToolConnectorProvider();
            var iToolClusterClient = await iToolConnectorProvider.CreateICluster();
            services.AddSingleton(iToolConnectorProvider);

            services.AddSingleton(iToolClusterClient);
            services.AddSingleton<AppSettingBuilder>();

            var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json", true, true);
            httpWorkerServices.AddSingleton(iToolClusterClient);
            httpWorkerServices.AddSingleton<IConfiguration>(builder.Build());
            
            return services;
        }

        private T GetService<T>() where T : class
        {
            return ProviderOfServices.GetService<T>();
        }

        private void WriteProcessState(ProcessStatusEnum processStatus)
        {
            if (!File.Exists(_iToolConnectorProcessPath))
            {
                File.Create(_iToolConnectorProcessPath).Close();
            }

            File.WriteAllText(_iToolConnectorProcessPath, processStatus.ToString());
        }


        private async Task RandomDelay(int max = 1111)
        {
            // 制造错峰
            await Task.Delay(new Random().Next(11, max));
        }

        private void StopSystemctl()
        {
            string stopiToolConnector = "systemctl stop iToolConnector.service";
            File.WriteAllText(_commandPath, stopiToolConnector);
            Executer.Run("sh " + _commandPath);
        }

    }
}
